/*******************************************************************************
* See cvimage.h
*  
* Licensed under GPL Version 3 license (/license.txt).
* Author: Eder A. Perez (eder.perez@ice.ufjf.br)
*******************************************************************************/



#include <string.h>
#include <stdlib.h>
#include <math.h>
//#include <jpeglib.h>
#include "cvimage.h"
#include "cvaux.h"



/*******************************************************************************/
/* Bitmap file format                                                          */
/*******************************************************************************/
#define BITMAP_SIGNATURE 0x4D42

// Compression method field (for biCompression field)
#define BI_RGB        0x0000
#define BI_RLE8       0x0001
#define BI_RLE4       0x0002
#define BI_BITFIELDS  0x0003
#define BI_JPEG       0x0004
#define BI_PNG        0x0005

// BMP header
#pragma pack(push,2)
typedef struct _bitmapfileheader {
  CVint16_t  bfType;     // It must be 'BM'
  CVint32_t  bfSize;     // The size of the file in bytes
  CVint16_t  bfReserved1, bfReserved2;
  CVint32_t  bfOffBits;  // The starting address of the byte where the bitmap data can be found.
} BITMAPFILEHEADER;
#pragma pack(pop)

// BMP DIB header
typedef struct _bitmapinfoheader {
  CVint32_t biSize;            // Should be set to sizeof(BITMAPINFOHEADER)
  CVint32_t biWidth, biHeight; // Define the width and the height of the bitmap in pixels
  CVint16_t biPlanes;          // Should always be 1.
  CVint16_t biBitCount;        // Defines the bpp value
  CVint32_t biCompression;     // Specifies the type of compression.
  CVint32_t biSizeImage;       // Should contain the size of the bitmap proper in bytes.
  CVint32_t biXPelsPerMeter, biYPelsPerMeter;
  CVint32_t biClrUsed;         // Provides a way for getting smaller color tables.
  CVint32_t biClrImportant;    // Specifies that the first x colors of the color
                             // table are important to the DIB.
} BITMAPINFOHEADER;



/*******************************************************************************/
/* Constructor                                                                 */
/*******************************************************************************/
CVImage::CVImage(): width(0), height(0), bpp(0), rowsize(0), data(NULL),
                    palclrs(0), palette(NULL), errorcode(CV_SUCCESS) {
}



/*******************************************************************************/
/* Destructor                                                                  */
/*******************************************************************************/
CVImage::~CVImage() {
  if(data)    free(data);
  if(palette) free(palette);
}



/*******************************************************************************/
/* public: Load a bitmap image                                                 */
/* @params                                                                     */
/*   filename: Path to the image file.                                         */
/*                                                                             */
/* @return: TRUE if image is successfully loaded. FALSE otherwise.             */
/*******************************************************************************/
bool CVImage::loadBMP(const char* filename) {

  errorcode = CVERR_UNKNOW;

  // Open image file
  FILE* bmp_file;
  if( !(bmp_file = fopen(filename, "rb")) ) {
    errorcode = CVERR_IMG_FILE_NOT_FOUND;
    return false;
  }

  // Read the headers
  BITMAPFILEHEADER bmp_header;
  BITMAPINFOHEADER dib_header;
  fread(&bmp_header, sizeof(BITMAPFILEHEADER), 1, bmp_file);
  fread(&dib_header, sizeof(BITMAPINFOHEADER), 1, bmp_file);
  
  // Verify the bitmap signature
  if(bmp_header.bfType != BITMAP_SIGNATURE) {
    fclose(bmp_file);
    errorcode = CVERR_IMG_BMP_SIGNATURE;
    return false;
  }

  // Verify the DIB header version
  if(dib_header.biSize != sizeof(BITMAPINFOHEADER)) {
    fclose(bmp_file);
    errorcode = CVERR_IMG_BMP_INVALID_DIB_HEADER;
    return false;
  }

  // Set attributes
  this->height  = (dib_header.biHeight > 0)? dib_header.biHeight: -dib_header.biHeight;
  this->width   = dib_header.biWidth;
  this->rowsize = ROWSIZE(dib_header.biWidth, dib_header.biBitCount);
  this->data    = (CVubyte_t*) malloc(sizeof(CVubyte_t)*this->rowsize*this->height);
  
  if(this->data == NULL) {
    fclose(bmp_file);
    errorcode = CVERR_MEM_NOT_ALLOCATED;
    return false;
  }
  
  this->bpp = dib_header.biBitCount;
  switch(this->bpp) {
    case 1:
      this->clr_fmt = CV_CLRFMT_RGB1;
      break;

    case 4:
      this->clr_fmt = CV_CLRFMT_RGB4;
      break;

    case 8:
      this->clr_fmt = CV_CLRFMT_RGB8;
      break;

    case 16:
      this->clr_fmt = CV_CLRFMT_RGB444;
      break;

    case 24:
      this->clr_fmt = CV_CLRFMT_RGB24;
      break;

    case 32:
      this->clr_fmt = CV_CLRFMT_RGB32;
      break;
      
    default: fclose(bmp_file); return false;  
  }

  // Unpack data
  switch(dib_header.biBitCount) {
    case 24:
      errorcode = unpack_bmp_24bpp(bmp_file, (dib_header.biHeight > 0)? true: false); break;
      
    case 16:
      errorcode = unpack_bmp_16bpp(bmp_file, (dib_header.biHeight > 0)? true: false); break;

    case 8:
      errorcode = unpack_bmp_8bpp(bmp_file, &dib_header, &bmp_header); break;

    case 4:
      errorcode = unpack_bmp_4bpp(bmp_file, &dib_header, &bmp_header); break;

    case 1:
      errorcode = unpack_bmp_1bpp(bmp_file, (dib_header.biHeight > 0)? true: false, &dib_header); break;

    default: errorcode = CVERR_IMG_BMP_INVALID_BPP; break;
  }

  fclose(bmp_file);
  if(errorcode != CV_SUCCESS) return false;
  else return true;
}



/*******************************************************************************/
/* private: Unpack 24bpp bmp data                                              */
/* @params                                                                     */
/*   data_file: Image file.                                                    */
/*   bottomup: TRUE if image's pixels starts from bottom.                      */
/*                                                                             */
/* @return: CVerror_t containing the operation status.                         */
/*******************************************************************************/
CVerror_t CVImage::unpack_bmp_24bpp(FILE* data_file, bool bottomup) {
  
  // Flip data if bottomup is false
  if(bottomup)
    fread(this->data, sizeof(CVubyte_t)*this->rowsize*this->height, 1, data_file);
  else {
    fseek(data_file, -this->rowsize, SEEK_END);
    long int back_step = (-this->rowsize) << 1;
    CVubyte_t* data_ptr = this->data;
    for(int j = 0; j < this->height; ++j) {
      fread(data_ptr, this->rowsize, 1, data_file);
      fseek(data_file, back_step, SEEK_CUR);
      data_ptr += this->rowsize;
    }
  }

  // Convert BGR to RGB
  for(int j = 0; j < this->height; ++j) {
    CVubyte_t* data_ptr = &(this->data[j*this->rowsize]);
    for(int i = 0; i < this->width; ++i, data_ptr += 3) {
      SWAP(data_ptr[0], data_ptr[2]);
    }
  }
  
  return CV_SUCCESS;
}



/*******************************************************************************/
/* private: Unpack 16bpp bmp data. Each channel (ARGB) has 4 bits              */
/* @params                                                                     */
/*   data_file: Image file.                                                    */
/*   bottomup: TRUE if image's pixels starts from bottom.                      */
/*                                                                             */
/* @return: CVerror_t containing the operation status.                         */
/*******************************************************************************/
CVerror_t CVImage::unpack_bmp_16bpp(FILE* data_file, bool bottomup) {
  
  // Flip data if bottomup is false
  if(bottomup)
    fread(this->data, sizeof(CVubyte_t)*this->rowsize*this->height, 1, data_file);
  else {
    fseek(data_file, -this->rowsize, SEEK_END);
    long int back_step = (-this->rowsize) << 1;
    CVubyte_t* data_ptr = this->data;
    for(int j = 0; j < this->height; ++j) {
      fread(data_ptr, this->rowsize, 1, data_file);
      fseek(data_file, back_step, SEEK_CUR);
      data_ptr += this->rowsize;
    }
  }

  // Convert BGR to RGB
  /*for(int j = 0; j < this->height; ++j) {
    CVubyte_t* data_ptr = &(this->data[j*this->rowsize]);
    for(int i = 0; i < this->width; ++i, data_ptr += 3) {
      SWAP(data_ptr[0], data_ptr[2]);
    }
  }*/
  
  return CV_SUCCESS;
}



/*******************************************************************************/
/* private: Unpack 8bpp bmp data                                               */
/* @params                                                                     */
/*   data_file: Image file.                                                    */
/*   dib_header: Information about the image.                                  */
/*   bmp_header: Information about the image file.                             */
/*                                                                             */
/* @return: CVerror_t containing the operation status.                         */
/*******************************************************************************/
CVerror_t CVImage::unpack_bmp_8bpp(FILE* data_file, void *dib_header, void *bmp_header) {
  BITMAPINFOHEADER *dib = (BITMAPINFOHEADER*) dib_header;
  BITMAPFILEHEADER *bmp = (BITMAPFILEHEADER*) bmp_header;

  // Load palette (only for 1-, 4- and 8-bpp images)
  int n_bytes = (dib->biClrUsed == 0)? (1 << dib->biBitCount)*4 : dib->biClrUsed*4;
  this->palclrs = n_bytes >> 2;
  this->palette = (CVubyte_t*) malloc(sizeof(CVubyte_t)*n_bytes);

  if(this->palette == NULL) return CVERR_MEM_NOT_ALLOCATED;

  fread(this->palette, sizeof(CVubyte_t)*n_bytes, 1, data_file);

  // Convert BGR to RGB palette
  for(int i = 0; i < n_bytes; i += 4) {
    SWAP(this->palette[i], this->palette[i+2]);
  }

  // Unpack uncompressed data
  if(dib->biCompression == BI_RGB) {
    // Flip data if bottomup is false
    if(dib->biHeight > 0)
      fread(this->data, sizeof(CVubyte_t)*this->rowsize*this->height, 1, data_file);
    else {
      fseek(data_file, -this->rowsize, SEEK_END);
      long int back_step = (-this->rowsize) << 1;
      CVubyte_t* data_ptr = this->data;
      for(int j = 0; j < this->height; ++j) {
        fread(data_ptr, this->rowsize, 1, data_file);
        fseek(data_file, back_step, SEEK_CUR);
        data_ptr += this->rowsize;
      }
    }
    return CV_SUCCESS;
  }
  // Unpack compressed data
  else if(dib->biCompression == BI_RLE8) {
    CVubyte_t* data_rle8 = (CVubyte_t*) malloc(sizeof(CVubyte_t)*(bmp->bfSize - bmp->bfOffBits));
    fread(data_rle8, sizeof(CVubyte_t)*(bmp->bfSize - bmp->bfOffBits), 1, data_file);
    
    int x = 0, y = 0, i = 0;
    register int not_eof = 1;
    while(not_eof) {
      int c = data_rle8[i++];
      
      if(c) {
        int n = c;
        c = data_rle8[i++];
        for(int j = 0; j < n; ++j) { this->data[y*this->rowsize + x] = c; ++x; }
      }
      else {
        c = data_rle8[i++];
        switch(c) {
          case 0: // EOL
            ++y; x = 0; break;

          case 1: // EOF
           not_eof = 0; break;

          case 2: // Delta
           x += (int) data_rle8[i++]; y += (int) data_rle8[i++]; break;

          default: // Absolute mode
              for(int j = 0; j < (int) c; ++j) { this->data[y*rowsize + x] = data_rle8[i++]; ++x; }
              if(c & 1) ++i; // Absolute mode has to be WORD aligned
              break;
        }
      }
    }
    free(data_rle8);
    return CV_SUCCESS;
  }

  return CVERR_IMG_UNSUPPORTED_FORMAT;
}



/*******************************************************************************/
/* private: Unpack 4bpp bmp data                                               */
/* @params                                                                     */
/*   data_file: Image file.                                                    */
/*   dib_header: Information about the image.                                  */
/*   bmp_header: Information about the image file.                             */
/*                                                                             */
/* @return: CVerror_t containing the operation status.                         */
/*******************************************************************************/
CVerror_t CVImage::unpack_bmp_4bpp(FILE* data_file, void *dib_header, void *bmp_header) {
  BITMAPINFOHEADER *dib = (BITMAPINFOHEADER*) dib_header;
  BITMAPFILEHEADER *bmp = (BITMAPFILEHEADER*) bmp_header;
  
  // Load palette (only for 1-, 4- and 8-bpp images)
  int n_bytes = (dib->biClrUsed == 0)? (1 << dib->biBitCount)*4 : dib->biClrUsed*4;
  this->palclrs = n_bytes >> 2;
  this->palette = (CVubyte_t*) malloc(sizeof(CVubyte_t)*n_bytes);
  fread(this->palette, sizeof(CVubyte_t)*n_bytes, 1, data_file);

  // Convert BGR to RGB palette
  for(int i = 0; i < n_bytes; i += 4) {
    SWAP(this->palette[i], this->palette[i+2]);
  }

  // Unpack uncompressed data
  if(dib->biCompression == BI_RGB) {
    // Flip data if bottomup is false
    if(dib->biHeight > 0)
      fread(this->data, sizeof(CVubyte_t)*this->rowsize*this->height, 1, data_file);
    else {
      fseek(data_file, -this->rowsize, SEEK_END);
      long int back_step = (-this->rowsize) << 1;
      CVubyte_t* data_ptr = this->data;
      for(int j = 0; j < this->height; ++j) {
        fread(data_ptr, this->rowsize, 1, data_file);
        fseek(data_file, back_step, SEEK_CUR);
        data_ptr += this->rowsize;
      }
    }
    return CV_SUCCESS;

  }
  else if(dib->biCompression == BI_RLE4) { // Unpack compressed data
    CVubyte_t* data_rle4 = (CVubyte_t*) malloc(sizeof(char)*(bmp->bfSize - bmp->bfOffBits));
    fread(data_rle4, sizeof(char)*(bmp->bfSize - bmp->bfOffBits), 1, data_file);
    memset(this->data, 0, sizeof(char)*this->rowsize*this->height);
    
    int x = 0, y = 0, i = 0;
    register int not_eof = 1;
    register int nib_test = 3;
    CVubyte_t k, pxls;
    while(not_eof) {
      int c = data_rle4[i++];

      if(c) {
        pxls = data_rle4[i++];
        nib_test |= 0x01; // State becomes x1 (see next comment)
        for(int j = 0; j < (int) c; ++j) {
          // Here we use the two less significant bits of nib_test to represent
          // the state of data and pxls byte, respectively. If the respective
          // bit is on, we access/write the left-most nibble. Otherwise,
          // we access/write the right-most nibble.
          switch(nib_test) {
            case 0: // 00
              k = pxls & 0x0F; nib_test = 3; break;
            case 1: // 01
              k = pxls >> 4; nib_test = 2; break;
            case 2: // 10
              k = pxls << 4; nib_test = 1; break;
            case 3: // 11
              k = pxls & 0xF0; nib_test = 0; break;
          }
          this->data[y*rowsize + x] |= k;
          if(nib_test & 0x02) ++x;
        }
       }
      else {
        c = data_rle4[i++];
        int stepx;
        switch(c) {
          case 0: // EOL
            ++y; x = 0; nib_test |= 0x02; break;
          
          case 1: // EOF
            not_eof = 0; break;
            
          case 2: // Delta
            stepx = data_rle4[i++];
            if(stepx & 1) {
              stepx = (nib_test & 0x02)? (--stepx) >> 1: (++stepx) >> 1;
              nib_test ^= 0x02; // Inverts just the second bit
            }
            else stepx = stepx >> 1;
            
            x += stepx; y += (int) data_rle4[i++];
            break;
            
          default: // Absolute mode
            nib_test |= 0x01;
            for(int j = 0; j < (int) c; ++j ) {
              if( (j % 2) == 0) pxls = data_rle4[i++];
              switch(nib_test) {
                case 0: // 00
                  k = pxls & 0x0F; nib_test = 3; break;
                case 1: // 01
                  k = pxls >> 4; nib_test = 2; break;
                case 2: // 10
                  k = pxls << 4; nib_test = 1; break;
                case 3: // 11
                  k = pxls & 0xF0; nib_test = 0; break;
              }
              this->data[y*rowsize + x] |= k;
              if(nib_test & 0x02) ++x;
              
            }
            if(i & 1) ++i; // Absolute mode has to be WORD aligned
            break;
        }
      }
    }
    free(data_rle4);
    return CV_SUCCESS;
    
  }

  return CVERR_IMG_UNSUPPORTED_FORMAT;
}



/*******************************************************************************/
/* private: Unpack 1bpp bmp data                                               */
/* @params                                                                     */
/*   data_file: Image file.                                                    */
/*   bottomup: TRUE if image's pixels starts from bottom.                      */
/*   dib_header: Information about the image.                                  */
/*                                                                             */
/* @return: CVerror_t containing the operation status.                         */
/*******************************************************************************/
CVerror_t CVImage::unpack_bmp_1bpp(FILE* data_file, bool bottomup, void *dib_header) {
  BITMAPINFOHEADER *dib = (BITMAPINFOHEADER*) dib_header;

  // Load palette (only for 1-, 4- and 8-bpp images)
  int n_bytes = (dib->biClrUsed == 0)? (1 << dib->biBitCount)*4 : dib->biClrUsed*4;
  this->palclrs = n_bytes >> 2;
  this->palette = (CVubyte_t*) malloc(sizeof(CVubyte_t)*n_bytes);
  fread(this->palette, sizeof(CVubyte_t)*n_bytes, 1, data_file);

  // Convert BGR to RGB palette
  for(int i = 0; i < n_bytes; i += 4) {
    SWAP(this->palette[i], this->palette[i+2]);
  }
  
  // Flip data if bottomup is false
  if(bottomup)
    fread(this->data, sizeof(CVubyte_t)*this->rowsize*this->height, 1, data_file);
  else {
    fseek(data_file, -this->rowsize, SEEK_END);
    long int back_step = (-this->rowsize) << 1;
    CVubyte_t* data_ptr = this->data;
    for(int j = 0; j < this->height; ++j) {
      fread(data_ptr, this->rowsize, 1, data_file);
      fseek(data_file, back_step, SEEK_CUR);
      data_ptr += this->rowsize;
    }
  }

  return CV_SUCCESS;
}



/*******************************************************************************/
/* public: Save a bmp image                                                    */
/* @params                                                                     */
/*   filename: Path to the image file.                                         */
/*                                                                             */
/* @return: TRUE if image was successfully saved. FALSE otherwise.             */
/*******************************************************************************/
bool CVImage::saveBMP(const char* filename) {
  int tmp_data_size = sizeof(CVubyte_t)*this->rowsize*this->height;

  //Verify data
  if( (width <= 0) || (height <= 0) || (data == NULL) || (bpp <= 0) ) {
    errorcode = CVERR_IMG_UNSUPPORTED_FORMAT;
    return false;
  }

  //Create headers
  BITMAPFILEHEADER bmp_header;
  BITMAPINFOHEADER dib_header;

  bmp_header.bfType    = BITMAP_SIGNATURE;
  bmp_header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (this->palclrs << 2);
  bmp_header.bfSize    = bmp_header.bfOffBits + tmp_data_size;

  dib_header.biSize          = sizeof(BITMAPINFOHEADER);
  dib_header.biWidth         = this->width;
  dib_header.biHeight        = this->height;
  dib_header.biPlanes        = 1;
  dib_header.biBitCount      = this->bpp;
  dib_header.biCompression   = BI_RGB;
  dib_header.biSizeImage     = tmp_data_size;
  dib_header.biXPelsPerMeter = dib_header.biYPelsPerMeter = 0;
  dib_header.biClrUsed       = this->palclrs;
  dib_header.biClrImportant  = 0;

  // Write data headers in the file
  FILE* bmp_file;
  if( !(bmp_file = fopen(filename, "wb")) ) {
    errorcode = CVERR_IMG_FILE_NOT_CREATED;
    return false;
  }
  fwrite(&bmp_header, sizeof(BITMAPFILEHEADER), 1, bmp_file);
  fwrite(&dib_header, sizeof(BITMAPINFOHEADER), 1, bmp_file);

  // Convert palette from RGB to BGR and writes it in the file
  if(this->palette) {
    CVubyte_t* tmp_pal = (CVubyte_t*) malloc(sizeof(CVubyte_t)*(this->palclrs << 2));
    memcpy(tmp_pal, this->palette, sizeof(CVubyte_t)*(this->palclrs << 2));
    for(int i = 0; i < (this->palclrs << 2); i += 4) {
      SWAP(tmp_pal[i], tmp_pal[i+2]);
    }
    fwrite(tmp_pal, sizeof(CVubyte_t)*(this->palclrs << 2), 1, bmp_file);
    free(tmp_pal);
  }

  // Convert data from RGB to BGR (if bpp > 8)
  if(this->bpp > 8) {
    CVubyte_t* tmp_data = (CVubyte_t*) malloc(tmp_data_size);
    memcpy(tmp_data, this->data, tmp_data_size);
    for(int j = 0; j < this->height; ++j) {
      CVubyte_t* data_ptr = &(tmp_data[j*this->rowsize]);
      for(int i = 0; i < this->width; ++i, data_ptr += 3) {
        SWAP(data_ptr[0], data_ptr[2]);
      }
    }
    fwrite(tmp_data, tmp_data_size, 1, bmp_file);
    free(tmp_data);
  }
  else fwrite(this->data, tmp_data_size, 1, bmp_file);
  
  fclose(bmp_file);
  errorcode = CV_SUCCESS;
  return true;
}



/*******************************************************************************/
/* public: Load a jpg image                                                    */
/* @params                                                                     */
/*   filename: Path to the image file.                                         */
/*                                                                             */
/* @return: TRUE if image was successfully loaded. FALSE otherwise.             */
/*******************************************************************************/
bool CVImage::loadJPG(const char* filename) {
/*  struct jpeg_decompress_struct cinfo;
  struct jpeg_error_mgr jerr;
  
  // Try to open jpeg file
  FILE* jpg_file;
  if( (jpg_file = fopen(filename, "rb")) == NULL ) {
    errorcode = CVERR_IMG_FILE_NOT_FOUND;
    return false;
  }

  // Allocate and initialize JPEG decompression object  
  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_decompress(&cinfo);
  jpeg_stdio_src(&cinfo, jpg_file);
  
  // Read header  
  jpeg_read_header(&cinfo, TRUE);
  
  // Start decompressor
  jpeg_start_decompress(&cinfo);
  
  // Set CVImage object attributes
  this->width   = cinfo.output_width;
  this->height  = cinfo.output_height;
  this->bpp     = (cinfo.out_color_components == 3)? 24 : 8; // RGB or Grayscale
  this->rowsize = ROWSIZE(width, bpp);
  this->data    = (CVubyte_t*) malloc(sizeof(CVubyte_t)*rowsize*height);
  
  // Decompress data each line at a time
  JSAMPROW buffer[1];
  int h = height-1;
  buffer[0] = &data[(h--)*rowsize];
  while(cinfo.output_scanline < cinfo.output_height) {
    jpeg_read_scanlines(&cinfo, buffer, 1);
    buffer[0] = &data[(h--)*rowsize];
  }
  
  // Finish decompression and free data structures
  jpeg_finish_decompress(&cinfo);
  jpeg_destroy_decompress(&cinfo);
  
  fclose(jpg_file);
  errorcode = CV_SUCCESS;*/
  return true;
}



/*******************************************************************************/
/* public: Save a jpg image                                                    */
/* @params                                                                     */
/*   filename: Path to the image file.                                         */
/*   quality:  Compression quality.                                            */
/*                                                                             */
/* @return: TRUE if image was successfully saved. FALSE otherwise.             */
/*******************************************************************************/
bool CVImage::saveJPG(const char* filename, int quality) {
  /*struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;

  // Try create a jpeg file
  FILE* jpg_file;
  if ((jpg_file = fopen(filename, "wb")) == NULL) {
    errorcode = CVERR_IMG_FILE_NOT_CREATED;
    return false;
  }
  
  // Allocate and initialize JPGE compression object
  cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_compress(&cinfo);
  jpeg_stdio_dest(&cinfo, jpg_file);

  // Set parameters for compression
  cinfo.image_width      = this->width;
  cinfo.image_height     = this->height;
  cinfo.input_components = 3;
  cinfo.in_color_space   = JCS_RGB;
  
  // Set defaults compression parameters
  jpeg_set_defaults(&cinfo);
  
  // Set quality (quantization table) scaling
  jpeg_set_quality(&cinfo, quality, TRUE);

  // Start compressor
  jpeg_start_compress(&cinfo, TRUE);

  // Compress data each row at a time
  int row_stride = this->width * 3;
  int h = this->height-1;
  JSAMPROW row_pointer[1];
  while(cinfo.next_scanline < cinfo.image_height) {
    row_pointer[0] = &data[(h--)*rowsize];
    jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }

  // Finish compression and release JPEG compression object
  jpeg_finish_compress(&cinfo);
  jpeg_destroy_compress(&cinfo);

  fclose(jpg_file);
  errorcode = CV_SUCCESS;*/
  return true;
}



/*******************************************************************************/
/* public: Create an new image                                                 */
/* @params                                                                     */
/*   width: Image width.                                                       */
/*   height: Image height.                                                     */
/*   clr_fmt: Color format.                                                    */
/*******************************************************************************/
bool CVImage::createImage(unsigned int width, unsigned int height, CVcolor_fmt_t clr_fmt) {

  errorcode = CVERR_UNKNOW;
  if(this->data)    free(this->data);
  if(this->palette) free(this->palette);
  
  this->clr_fmt = clr_fmt;
  switch(clr_fmt) {
    // 1bpp
    case CV_CLRFMT_RGB1:
      this->bpp = 1;
      break;

    // 4bpp
    case CV_CLRFMT_RGB4:
      this->bpp = 4;
      break;

    // 8bpp
    case CV_CLRFMT_RGB8:
    case CV_CLRFMT_GRAY8:
      this->bpp = 8;
      break;
    
    // 16bpp
    case CV_CLRFMT_RGB444:
    case CV_CLRFMT_RGB555:
    case CV_CLRFMT_RGB565:
      this->bpp = 16;
      break;
    
    // 24bpp
    case CV_CLRFMT_RGB24:
    case CV_CLRFMT_YUV24:
      this->bpp = 24;
      break;
    
    // 32bpp
    case CV_CLRFMT_RGB32:
      this->bpp = 32;
      break;
  
    default: return false;
  }
  
  this->width   = width;
  this->height  = height;
  this->rowsize = ROWSIZE(width, bpp);
  this->data    = (CVubyte_t*) malloc(sizeof(CVubyte_t)*rowsize*height);
  
  if(this->data == NULL) {
    errorcode = CVERR_MEM_NOT_ALLOCATED;
    return false;
  }
  
  if(bpp < 16) {
    this->palclrs = 1 << bpp;
    this->palette = (CVubyte_t*) malloc(sizeof(CVubyte_t)*palclrs*4);
    
    if(this->palette == NULL) {
      errorcode = CVERR_MEM_NOT_ALLOCATED;
      return false;
    }
  }
  
  errorcode = CV_SUCCESS;
  return true;
}

